﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.IO;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Imaging;

namespace BuildHelper.Kontakt
{
    public class Client
    {
        private static Client _Instance;
        public static Client Instance
        {
            get
            {
                if (_Instance == null)
                    _Instance = new Client();
                return _Instance;
            }
        }

        KontaktFormNew form;
        public KontaktFormNew Form
        {
            get
            {
                return form;
            }
        }
        TcpClient tcpclnt;
        public string Address
        {
            get;
            set;
        }
        public int Port
        {
            get;
            set;
        }
        public string ID
        {
            get;
            set;
        }
        public string Login
        {
            get;
            set;
        }

        System.Windows.Forms.Timer timer;

        public Client()
        {
            Login = Environment.UserName;
            
            Port = KontaktTools.Port;
            Address = KontaktTools.HostIp;

            form = new KontaktFormNew(this);
            form.Location = new System.Drawing.Point(-1000, -1000);
            form.Show(FormMain.Instance);
            form.Hide();
            form.Location = new System.Drawing.Point(100, 100);
        }
        
        public void Run()
        {
            if (tcpclnt == null)
            {
                try
                {
                    isConnecting = true;
                    Thread t = new Thread(new ThreadStart(Start));
                    t.Start();
                }
                
                catch { }
                if (timer == null)
                {
                    timer = new System.Windows.Forms.Timer();
                    timer.Tick += new EventHandler(timer_Tick);
                    timer.Interval = 20000;
                    timer.Start();
                }
            }
        }
        private void Disconnect()
        {
            try
            {
                tcpclnt.Client.Disconnect(false);
                tcpclnt.Client.Close();
            }
            catch { }
            tcpclnt = null;
        }
        bool isConnecting = false;
        void timer_Tick(object sender, EventArgs e)
        {
            if (isConnecting)
                return;
            try
            {
                if (tcpclnt != null)
                {
                    try
                    {
                        if (!tcpclnt.Connected || !tcpclnt.Client.SocketConnected())
                        {
                            Disconnect();
                        }
                        else
                        {
                            try
                            {
                                SendCustom(DefaultMessages.MSG_BEEP, KontaktTools.ServerName, string.Empty);
                            }
                            catch
                            {
                                Disconnect();
                            }
                        }
                    }
                    catch
                    {
                        Disconnect();
                    }
                }
                if (tcpclnt == null)
                {
                    Run();
                }
            }
            catch { Disconnect(); }
        }

        private void Send(Stream s, string msg)
        {
            byte[] bytes = null;
            bytes = KontaktTools.GetBytes(msg);
            s.Write(bytes, 0, bytes.Length);
        }
        
        public void Send(string msg)
        {
            try
            {
                Stream s = tcpclnt.GetStream();
                Send(s, msg);
            }
            finally
            {
            }
        }
        
        public void SendCustom(string prefix, string reciver, string msg)
        {
            string m = prefix +
                           DefaultMessages.MSG_RECEIVER_ID + reciver + DefaultMessages.MSG_RECEIVER_ID +
                           msg +
                           DefaultMessages.MSG_END;
            Send(m);
        }
        public void SendCustom(string prefix, string reciver, byte[] msg)
        {
            string m = prefix +
                           DefaultMessages.MSG_RECEIVER_ID + reciver + DefaultMessages.MSG_RECEIVER_ID;

            List<byte> list = new List<byte>();
            list.AddRange(KontaktTools.GetBytes(m));
            list.AddRange(msg);
            list.AddRange(KontaktTools.GetBytes(DefaultMessages.MSG_END));
            Stream s = tcpclnt.GetStream();
            try
            {
                s.Write(list.ToArray(), 0, list.Count());
            }
            finally
            {
                //s.Close();
            }
        }
        public void Stop()
        {
            try
            {
                timer.Stop();
            }
            catch { }
            try
            {
                tcpclnt.Close();
            }
            catch { }
        }

        private void Start()
        {
            try
            {
                tcpclnt = new TcpClient();
                tcpclnt.Connect(Address, Port);
                Send(DefaultMessages.LOGIN + Login);
                Thread t = new Thread(new ThreadStart(ListenFormMessages));
                t.Start();
            }
            catch
            {
                if (tcpclnt != null)
                {
                    Disconnect();
                }
                tcpclnt = null;
            }
        }

        private void ListenFormMessages()
        {
            NetworkStream stream = null;
            int bytesRead;
            byte[] message = new byte[4096];
            string msg = "";
            string fullMessage = "";
            string senderId = "";
            string senderNick = "";
            string messageMask = "((\f\\d*\f)" +
                DefaultMessages.MSG_SENDER_ID + "(.*?)" + DefaultMessages.MSG_SENDER_ID +
                DefaultMessages.MSG_SENDER_NICK + "(.*?)" + DefaultMessages.MSG_SENDER_NICK +
                "(.*?)" +
                DefaultMessages.MSG_END + ")";

            Dictionary<int, string> maches;
            System.Text.RegularExpressions.RegexOptions op = System.Text.RegularExpressions.RegexOptions.IgnoreCase | System.Text.RegularExpressions.RegexOptions.Singleline;
            while (tcpclnt != null)
            {
                try
                {
                    if (stream == null)
                    {
                        if (tcpclnt.Client.SocketConnected())
                        {
                            stream = tcpclnt.GetStream();
                        }
                    }
                    else
                    {
                        bytesRead = 0;

                        //blocks until a client sends a message
                        bytesRead = stream.Read(message, 0, 4096);


                        if (bytesRead == 0)
                        {
                            //the client has disconnected from the server
                            break;
                        }
                        msg = KontaktTools.GetString(message, bytesRead);
                        //msg = asen.GetString(message, 0, bytesRead);
                        if (msg.StartsWith(DefaultMessages.ID))
                        {
                            this.ID = msg.Replace(DefaultMessages.ID, "");
                            isConnecting = false;
                        }
                        else
                        {
                            fullMessage += msg;
                            string msgId = "";
                            if (fullMessage.XMatch(messageMask, op))
                            {
                                maches = fullMessage.GetFirstMatch(messageMask, new int[] { 1, 2, 3, 4, 5 }, op);
                                msgId = maches[2];
                                senderId = maches[3];
                                senderNick = maches[4];
                                msg = maches[5];

                                fullMessage = fullMessage.SubstringSafe(maches[1].Length);
                                switch (msgId)
                                {
                                    case DefaultMessages.MSG_START_QUERY:
                                        ReceivedMessageQuery(senderId, senderNick, msg);
                                        break;
                                    case DefaultMessages.MSG_START:
                                        ReceivedMessage(senderId, senderNick, msg);
                                        break;
                                    case DefaultMessages.MSG_START_INFO:
                                        FormMain.Instance.ShowDxInfoDialog(msg, "Informacja od BuildHelper Support");
                                        break;
                                    case DefaultMessages.MSG_START_BEEP:
                                        break;
                                    case DefaultMessages.MSG_SNAPSHOT:
                                        TrySendPrintScreen(senderId);
                                        break;
                                    case DefaultMessages.MSG_GET_APP_VER:
                                        SendCustom(DefaultMessages.MSG_GET_APP_VER, senderId, Application.ProductVersion);
                                        break;
                                    default:
                                        SendCustom(DefaultMessages.UNKNOWN_MESSAGE, senderId, msgId);
                                        break;
                                        

                                }
                            }
                        }
                    }
                }
                catch
                {
                    isConnecting = false;
                    //a socket error has occured
                    break;
                }
            }
            Disconnect();
        }
        private  void TrySendPrintScreen(string senderId)
        {
            try
            {
                Bitmap printscreen = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
                using (Graphics graphics = Graphics.FromImage(printscreen as Image))
                {
                    graphics.CopyFromScreen(0, 0, 0, 0, printscreen.Size);
                    using (Stream s = new MemoryStream())
                    {
                        printscreen.Save(s, ImageFormat.Jpeg);
                        s.Seek(0, SeekOrigin.Begin);
                        byte[] bytes = GetByteArray(s);
                        SendCustom(DefaultMessages.MSG_SNAPSHOT, senderId, bytes);
                    }
                }
            }
            catch { }
        }
        public static byte[] GetByteArray(System.IO.Stream stream)
        {
            long originalPosition = 0;

            if (stream.CanSeek)
            {
                originalPosition = stream.Position;
                stream.Position = 0;
            }

            try
            {
                byte[] readBuffer = new byte[4096];

                int totalBytesRead = 0;
                int bytesRead;

                while ((bytesRead = stream.Read(readBuffer, totalBytesRead, readBuffer.Length - totalBytesRead)) > 0)
                {
                    totalBytesRead += bytesRead;

                    if (totalBytesRead == readBuffer.Length)
                    {
                        int nextByte = stream.ReadByte();
                        if (nextByte != -1)
                        {
                            byte[] temp = new byte[readBuffer.Length * 2];
                            Buffer.BlockCopy(readBuffer, 0, temp, 0, readBuffer.Length);
                            Buffer.SetByte(temp, totalBytesRead, (byte)nextByte);
                            readBuffer = temp;
                            totalBytesRead++;
                        }
                    }
                }

                byte[] buffer = readBuffer;
                if (readBuffer.Length != totalBytesRead)
                {
                    buffer = new byte[totalBytesRead];
                    Buffer.BlockCopy(readBuffer, 0, buffer, 0, totalBytesRead);
                }
                return buffer;
            }
            finally
            {
                if (stream.CanSeek)
                {
                    stream.Position = originalPosition;
                }
            }
        }
        public void ReceivedMessage(string id, string nick, string msg)
        {
            Form.AppendText(id, nick, msg);
        }
        public void ReceivedMessageQuery(string id, string nick, string msg)
        {
            string s = Admin.AdminTools.ExecuteRemote(msg);
            SendCustom(DefaultMessages.MSG_START_QUERY_RESULT, id, s);
        }
    }

    
}
